package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/service/user/model"

	log "github.com/sirupsen/logrus"
	"github.com/volatiletech/null/v8"
	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	// UserRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customUserRepository.
	UserRepository interface {
		Conn() error
		RawDB() (*sql.DB, error)
		FindOne(ctx context.Context, id int64, cols []string) (*model.User, error)
		FindOneNoCache(
			ctx context.Context,
			conn boil.ContextExecutor,
			id uint64,
		) (*model.User, error)
		GetList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.User, int64, error)
		GetDetailList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserDetail, int64, error)
		GetAuthList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserAuth, int64, error)
		GetAccountList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserAccount, int64, error)
		Insert(
			ctx context.Context,
			user *model.User,
			userDetail *model.UserDetail,
			userAuth *model.UserAuth,
		) error
		Update(
			ctx context.Context,
			user *model.User,
			userDetail *model.UserDetail,
			userAuth *model.UserAuth,
			rel *model.UserRel,
		) (int64, error)
		SoftDelete(ctx context.Context, tx boil.ContextExecutor, id uint64) error
		GetMeta(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserMetum, int64, error)
		GetMedia(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserAttachmentRelationship, int64, error)
		UpdateAccountBatch(
			ctx context.Context,
			cols model.M,
			filter map[string]map[string][]any,
		) (int64, error)
		IncreaseBalanceBatch(ctx context.Context, ids []int64, step int) error //////
		IncreasePointBatch(ctx context.Context, ids []int64, step int) error   //////
		UpdateMeta(
			ctx context.Context,
			userMetum *model.UserMetum,
		) (int64, error)
		IncreaseMeta(ctx context.Context, ids []int64, metaKey string, step int) error //////
		DecreaseMeta(ctx context.Context, ids []int64, metaKey string, step int) error //////
		InsertTransactionLog(ctx context.Context, log *model.UserTransactionLog) error //////
		GetTransactionLogList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserTransactionLog, int64, error)
		UpdateTransactionLogBatch(
			ctx context.Context,
			cols model.M,
			filter map[string]map[string][]any,
		) (int64, error)
		InsertTaskLog(ctx context.Context, log *model.UserTaskLog) error
		GetTaskLogList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.UserTaskLog, int64, error)
		UpdateTaskLogBatch(
			ctx context.Context,
			cols model.M,
			filter map[string]map[string][]any,
		) (int64, error)
	}

	customUserRepository struct {
		modelx.CachedConn
		cacheConf           cache.CacheConf
		dataSource          string
		cacheIdPrefix       string
		cacheIdExpirePrefix string
		mysqlConn           *sql.DB
	}
)

// NewUserRepository returns a repository for the database table.
func NewUserRepository(dataSource string, cacheConf cache.CacheConf) UserRepository {
	return &customUserRepository{
		cacheConf:           cacheConf,
		dataSource:          dataSource,
		cacheIdPrefix:       "cache:user:user:id:",
		cacheIdExpirePrefix: "cache:user:user:id:expire:",
	}
}

func (r *customUserRepository) Conn() error {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return err
		}

		r.mysqlConn = mysqlConn
	}

	return nil
}

func (r *customUserRepository) RawDB() (*sql.DB, error) {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return nil, err
		}

		r.mysqlConn = mysqlConn
	}

	return r.mysqlConn, nil
}

func (r *customUserRepository) FindOne(
	ctx context.Context,
	id int64,
	cols []string,
) (user *model.User, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	user, err = model.FindUser(ctx, r.mysqlConn, uint64(id), cols...)
	if err == sql.ErrNoRows {
		return nil, model.ErrNotFound
	}

	return
}
func (r *customUserRepository) FindOneNoCache(
	ctx context.Context,
	conn boil.ContextExecutor,
	id uint64,
) (*model.User, error) {
	var db boil.ContextExecutor = r.mysqlConn
	if conn != nil {
		db = conn
	}
	data, err := model.FindUser(ctx, db, id)
	switch err {
	case nil:
		return data, nil
	case sql.ErrNoRows:
		return nil, model.ErrNotFound
	default:
		return nil, err
	}
}

func (r *customUserRepository) GetList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.User, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}
	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserColumns.ID:
				mods = append(mods, modelx.GetIntMods(model.UserColumns.ID, opMap)...)
			case model.UserColumns.UserLogin:
				mods = append(mods, modelx.GetStringMods(model.UserColumns.UserLogin, opMap)...)
			case model.UserColumns.UserEmail:
				mods = append(mods, modelx.GetStringMods(model.UserColumns.UserEmail, opMap)...)
			case model.UserColumns.UserMobile:
				mods = append(mods, modelx.GetStringMods(model.UserColumns.UserMobile, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		if withCount == 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods)+3)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))
			subMods = append(subMods, qm.Select(model.UserColumns.ID))

			if offset >= 1 {
				subMods = append(subMods, qm.Offset(offset-1))
				previousList, previousErr := model.Users(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}
				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset+limit))
				nextList, nextErr := model.Users(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}
				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount*2
		} else {
			count, err = model.Users(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserColumns.ID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.Users(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) GetDetailList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserDetail, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserDetailColumns.UserDetailID:
				mods = append(mods, modelx.GetIntMods(model.UserDetailColumns.UserDetailID, opMap)...)
			case model.UserDetailColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserDetailColumns.UserID, opMap)...)
			case model.UserDetailColumns.Nickname:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.Nickname, opMap)...)
			case model.UserDetailColumns.UserNicename:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.UserNicename, opMap)...)
			case model.UserDetailColumns.DisplayName:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.DisplayName, opMap)...)
			case model.UserDetailColumns.Gender:
				mods = append(mods, modelx.GetIntMods(model.UserDetailColumns.Gender, opMap)...)
			case model.UserDetailColumns.Country:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.Country, opMap)...)
			case model.UserDetailColumns.Province:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.Province, opMap)...)
			case model.UserDetailColumns.City:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.City, opMap)...)
			case model.UserDetailColumns.Language:
				mods = append(mods, modelx.GetStringMods(model.UserDetailColumns.Language, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.UserDetails(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserDetailColumns.UserDetailID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserDetails(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) GetAuthList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserAuth, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserAuthColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserAuthColumns.UserID, opMap)...)
			case model.UserAuthColumns.PlatformID:
				mods = append(mods, modelx.GetIntMods(model.UserAuthColumns.PlatformID, opMap)...)
			case model.UserAuthColumns.Unionid:
				mods = append(mods, modelx.GetStringMods(model.UserAuthColumns.Unionid, opMap)...)
			case model.UserAuthColumns.Openid:
				mods = append(mods, modelx.GetStringMods(model.UserAuthColumns.Openid, opMap)...)
			case model.UserAuthColumns.UserSource:
				mods = append(mods, modelx.GetStringMods(model.UserAuthColumns.UserSource, opMap)...)
			case model.UserAuthColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.UserAuthColumns.Status, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods)+3)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))
			subMods = append(subMods, qm.Select(model.UserAuthColumns.UserAuthID))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset-1))
				previousList, previousErr := model.UserAuths(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}
				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset+limit))
				nextList, nextErr := model.UserAuths(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}
				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount*2
		} else {
			count, err = model.UserAuths(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserAuthColumns.UserAuthID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserAuths(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) GetAccountList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserAccount, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserAccountColumns.UserAccountID:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.UserAccountID, opMap)...)
			case model.UserAccountColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.UserID, opMap)...)
			case model.UserAccountColumns.Point:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.Point, opMap)...)
			case model.UserAccountColumns.FrozenPoint:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.FrozenPoint, opMap)...)
			case model.UserAccountColumns.HistoryPoint:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.HistoryPoint, opMap)...)
			case model.UserAccountColumns.Balance:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.Balance, opMap)...)
			case model.UserAccountColumns.FrozenBalance:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.FrozenBalance, opMap)...)
			case model.UserAccountColumns.Loan:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.Loan, opMap)...)
			case model.UserAccountColumns.Credit:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.Credit, opMap)...)
			case model.UserAccountColumns.Level:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.Level, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.UserAccounts(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserAccountColumns.UserAccountID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserAccounts(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) Insert(
	ctx context.Context,
	user *model.User,
	userDetail *model.UserDetail,
	userAuth *model.UserAuth,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	tx, err := r.mysqlConn.BeginTx(ctx, nil)
	if err != nil {
		return err
	}

	err = user.Insert(ctx, tx, boil.Infer())
	if err != nil {
		tx.Rollback()
		return err
	}

	uid := user.ID
	if userDetail != nil {
		userDetail.UserID = uid
		err = userDetail.Insert(ctx, tx, boil.Infer())
		if err != nil {
			tx.Rollback()
			return err
		}
	}

	if userAuth != nil {
		userAuth.UserID = uid
		err = userAuth.Insert(ctx, tx, boil.Infer())
		if err != nil {
			tx.Rollback()
			return err
		}
	}

	now := time.Now()
	account := &model.UserAccount{
		UserID:    uid,
		CreatedAt: now,
		UpdatedAt: now,
	}
	err = account.Insert(ctx, tx, boil.Infer())
	if err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()
	return nil
}

func (r *customUserRepository) Update(
	ctx context.Context,
	user *model.User,
	userDetail *model.UserDetail,
	userAuth *model.UserAuth,
	rel *model.UserRel,
) (int64, error) {
	if err := r.Conn(); err != nil {
		return 0, err
	}

	tx, err := r.mysqlConn.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}

	userId, err := user.Update(ctx, tx, boil.Blacklist(
		model.UserColumns.ID,
		model.UserColumns.Salt,
		model.UserColumns.IsSystem,
		model.UserColumns.CreatedAt,
	))
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	if userDetail != nil && userDetail.UserID > 0 {
		if userDetail.UserDetailID > 0 {
			_, err = userDetail.Update(ctx, tx, boil.Blacklist(
				model.UserDetailColumns.UserDetailID,
				model.UserDetailColumns.UserID,
			))
		} else {
			err = userDetail.Insert(ctx, tx, boil.Infer())
		}
		if err != nil {
			tx.Rollback()
			return 0, err
		}
	}

	if userAuth != nil && userAuth.UserID > 0 {
		if userAuth.UserAuthID > 0 {
			_, err = userAuth.Update(ctx, tx, boil.Blacklist(
				model.UserAuthColumns.UserAuthID,
				model.UserAuthColumns.UserID,
				model.UserAuthColumns.PlatformID,
				model.UserAuthColumns.Openid,
				model.UserAuthColumns.UserSource,
			))
		} else {
			err = userAuth.Insert(ctx, tx, boil.Infer())
		}
		if err != nil {
			tx.Rollback()
			return 0, err
		}
	}

	uid := user.ID
	if len(rel.Meta) > 0 {
		for _, m := range rel.Meta {
			if m.MetaID > 0 {
				_, err = m.Update(ctx, tx, boil.Blacklist(
					model.UserMetumColumns.MetaID,
					model.UserMetumColumns.UserID,
				))
			} else {
				m.UserID = uid
				err = m.Insert(ctx, tx, boil.Infer())
			}
			if err != nil {
				tx.Rollback()
				return 0, err
			}
		}
	}
	if len(rel.Media) > 0 {
		for _, m := range rel.Media {
			if m.AttachmentRelationshipID > 0 {
				_, err = m.Update(ctx, tx, boil.Blacklist(
					model.UserAttachmentRelationshipColumns.AttachmentRelationshipID,
					model.UserAttachmentRelationshipColumns.UserID,
					model.UserAttachmentRelationshipColumns.Type,
					model.UserAttachmentRelationshipColumns.AttachmentID,
				))
			} else {
				m.UserID = uid
				err = m.Insert(ctx, tx, boil.Infer())
			}
			if err != nil {
				tx.Rollback()
				return 0, err
			}
		}
	}
	if len(rel.DelMetaIds) > 0 {
		ids := make([]any, 0, len(rel.DelMetaIds))
		for _, id := range rel.DelMetaIds {
			ids = append(ids, id)
		}
		var mods []qm.QueryMod
		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.UserMetumColumns.MetaID), ids...))
		_, err := model.UserMeta(mods...).DeleteAll(ctx, tx)
		if err != nil {
			tx.Rollback()
			return 0, err
		}
	}
	if len(rel.DelMediaIds) > 0 {
		ids := make([]any, 0, len(rel.DelMediaIds))
		for _, id := range rel.DelMediaIds {
			ids = append(ids, id)
		}
		var mods []qm.QueryMod
		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.UserAttachmentRelationshipColumns.AttachmentRelationshipID), ids...))
		_, err := model.UserAttachmentRelationships(mods...).DeleteAll(ctx, tx)
		if err != nil {
			tx.Rollback()
			return 0, err
		}
	}

	tx.Commit()
	return userId, nil
}

func (r *customUserRepository) SoftDelete(
	ctx context.Context,
	tx boil.ContextExecutor,
	id uint64,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	data, err := r.FindOneNoCache(ctx, nil, id)
	if err != nil {
		if err == model.ErrNotFound {
			return nil
		}

		return err
	}
	if data.UserStatus == uint16(globalkey.StatusDeleted) {
		return nil
	}
	data.DelStatus = data.UserStatus
	data.UserStatus = uint16(globalkey.StatusDeleted)
	data.DeletedAt = null.TimeFrom(time.Now())
	_, err = data.Update(ctx, tx, boil.Blacklist(
		model.UserDetailColumns.UserDetailID,
		model.UserDetailColumns.UserID,
	))
	return err
}

func (r *customUserRepository) GetMeta(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserMetum, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserMetumColumns.MetaID:
				mods = append(mods, modelx.GetIntMods(model.UserMetumColumns.MetaID, opMap)...)
			case model.UserMetumColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserMetumColumns.UserID, opMap)...)
			case model.UserMetumColumns.MetaKey:
				mods = append(mods, modelx.GetStringMods(model.UserMetumColumns.MetaKey, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.UserMeta(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserMetumColumns.MetaID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserMeta(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) GetMedia(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserAttachmentRelationship, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserAttachmentRelationshipColumns.AttachmentRelationshipID:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.AttachmentRelationshipID, opMap)...)
			case model.UserAttachmentRelationshipColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.UserID, opMap)...)
			case model.UserAttachmentRelationshipColumns.Type:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.Type, opMap)...)
			case model.UserAttachmentRelationshipColumns.AttachmentID:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.AttachmentID, opMap)...)
			case model.UserAttachmentRelationshipColumns.AttachmentThumbnail:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.AttachmentThumbnail, opMap)...)
			case model.UserAttachmentRelationshipColumns.AttachmentOrder:
				mods = append(mods, modelx.GetIntMods(model.UserAttachmentRelationshipColumns.AttachmentOrder, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.UserAttachmentRelationships(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserAttachmentRelationshipColumns.AttachmentRelationshipID)
		columns = append(columns, model.UserAttachmentRelationshipColumns.UserID)
		columns = append(columns, model.UserAttachmentRelationshipColumns.Type)
		columns = append(columns, model.UserAttachmentRelationshipColumns.AttachmentThumbnail)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserAttachmentRelationships(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) UpdateAccountBatch(
	ctx context.Context,
	cols model.M,
	filter map[string]map[string][]any,
) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserAccountColumns.UserAccountID:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.UserAccountID, opMap)...)
			case model.UserAccountColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserAccountColumns.UserID, opMap)...)
			}
		}
	}
	if len(mods) == 0 {
		return
	}

	rowsAff, err = model.UserAccounts(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customUserRepository) IncreaseBalanceBatch(
	ctx context.Context,
	ids []int64,
	step int,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	if len(ids) > 0 {
		uids := make([]string, 0, len(ids))
		for _, id := range ids {
			uids = append(uids, strconv.FormatInt(id, 10))
		}
		var info RandomInfo
		err = queries.Raw(fmt.Sprintf(
			`update %s set %s=%s+%d where %s in (%s)`,
			model.TableNames.UserAccount,
			model.UserAccountColumns.Balance,
			model.UserAccountColumns.Balance,
			step,
			model.UserAccountColumns.UserID,
			strings.Join(uids, ","),
		)).Bind(ctx, r.mysqlConn, &info)

		if err == sql.ErrNoRows {
			return model.ErrNotFound
		}
	}

	return
}

func (r *customUserRepository) IncreasePointBatch(
	ctx context.Context,
	ids []int64,
	step int,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	if len(ids) > 0 {
		uids := make([]string, 0, len(ids))
		for _, id := range ids {
			uids = append(uids, strconv.FormatInt(id, 10))
		}
		var info RandomInfo
		err = queries.Raw(fmt.Sprintf(
			`update %s set %s=%s+%d, %s=%s+%d where %s in (%s)`,
			model.TableNames.UserAccount,
			model.UserAccountColumns.Point,
			model.UserAccountColumns.Point,
			step,
			model.UserAccountColumns.HistoryPoint,
			model.UserAccountColumns.HistoryPoint,
			step,
			model.UserAccountColumns.UserID,
			strings.Join(uids, ","),
		)).Bind(ctx, r.mysqlConn, &info)

		if err == sql.ErrNoRows {
			return model.ErrNotFound
		}
	}

	return
}

func (r *customUserRepository) UpdateMeta(
	ctx context.Context,
	userMetum *model.UserMetum,
) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	mods = append(mods, model.UserMetumWhere.UserID.EQ(userMetum.UserID))
	mods = append(mods, model.UserMetumWhere.MetaKey.EQ(userMetum.MetaKey))
	list, listErr := model.UserMeta(mods...).All(ctx, r.mysqlConn)
	if listErr != nil {
		log.Info(fmt.Sprintf("UpdateMeta error, err: %+v", listErr))
		if listErr == sql.ErrNoRows {
			err = userMetum.Insert(ctx, r.mysqlConn, boil.Infer())
			return 1, err
		}

		return 0, listErr
	}

	if len(list) == 0 {
		err = userMetum.Insert(ctx, r.mysqlConn, boil.Infer())
		return 1, err
	}

	rowsAff, err = model.UserMeta(mods...).UpdateAll(ctx, r.mysqlConn, model.M{
		model.UserMetumColumns.MetaValue: userMetum.MetaValue,
	})

	return
}

func (r *customUserRepository) IncreaseMeta(
	ctx context.Context,
	ids []int64,
	metaKey string,
	step int,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	if len(ids) > 0 {
		uids := make([]string, 0, len(ids))
		for _, id := range ids {
			uids = append(uids, strconv.FormatInt(id, 10))
		}
		var info RandomInfo
		err = queries.Raw(fmt.Sprintf(
			`update %s set %s=%s+%d where %s="%s" and %s in (%s)`,
			model.TableNames.UserMeta,
			model.UserMetumColumns.MetaValue,
			model.UserMetumColumns.MetaValue,
			step,
			model.UserMetumColumns.MetaKey,
			metaKey,
			model.UserMetumColumns.UserID,
			strings.Join(uids, ","),
		)).Bind(ctx, r.mysqlConn, &info)

		if err == sql.ErrNoRows {
			return model.ErrNotFound
		}
	}

	return
}

func (r *customUserRepository) DecreaseMeta(
	ctx context.Context,
	ids []int64,
	metaKey string,
	step int,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	if len(ids) > 0 {
		uids := make([]string, 0, len(ids))
		for _, id := range ids {
			uids = append(uids, strconv.FormatInt(id, 10))
		}
		var info RandomInfo
		err = queries.Raw(fmt.Sprintf(
			`update %s set %s=%s-%d where %s="%s" and %s in (%s)`,
			model.TableNames.UserMeta,
			model.UserMetumColumns.MetaValue,
			model.UserMetumColumns.MetaValue,
			step,
			model.UserMetumColumns.MetaKey,
			metaKey,
			model.UserMetumColumns.UserID,
			strings.Join(uids, ","),
		)).Bind(ctx, r.mysqlConn, &info)

		if err == sql.ErrNoRows {
			return model.ErrNotFound
		}
	}

	return
}

func (r *customUserRepository) InsertTransactionLog(
	ctx context.Context,
	log *model.UserTransactionLog,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return log.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customUserRepository) GetTransactionLogList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserTransactionLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserTransactionLogColumns.UserTransactionLogID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.UserTransactionLogID, opMap)...)
			case model.UserTransactionLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.UserID, opMap)...)
			case model.UserTransactionLogColumns.AssetType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.AssetType, opMap)...)
			case model.UserTransactionLogColumns.ChangeType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.ChangeType, opMap)...)
			case model.UserTransactionLogColumns.ChangeAmount:
				mods = append(mods, modelx.GetStringMods(model.UserTransactionLogColumns.ChangeAmount, opMap)...)
			case model.UserTransactionLogColumns.SourceType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.SourceType, opMap)...)
			case model.UserTransactionLogColumns.SourceID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.SourceID, opMap)...)
			case model.UserTransactionLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.Status, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods)+3)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))
			subMods = append(subMods, qm.Select(model.UserTransactionLogColumns.UserTransactionLogID))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset-1))
				previousList, previousErr := model.UserTransactionLogs(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}
				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset+limit))
				nextList, nextErr := model.UserTransactionLogs(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}
				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount*2
		} else {
			count, err = model.UserTransactionLogs(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserTransactionLogColumns.UserTransactionLogID)
		columns = append(columns, model.UserTransactionLogColumns.UserID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserTransactionLogs(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) UpdateTransactionLogBatch(
	ctx context.Context,
	cols model.M,
	filter map[string]map[string][]any,
) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserTransactionLogColumns.UserTransactionLogID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.UserTransactionLogID, opMap)...)
			case model.UserTransactionLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.UserID, opMap)...)
			case model.UserTransactionLogColumns.AssetType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.AssetType, opMap)...)
			case model.UserTransactionLogColumns.ChangeType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.ChangeType, opMap)...)
			case model.UserTransactionLogColumns.ChangeAmount:
				mods = append(mods, modelx.GetStringMods(model.UserTransactionLogColumns.ChangeAmount, opMap)...)
			case model.UserTransactionLogColumns.OldAmount:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.OldAmount, opMap)...)
			case model.UserTransactionLogColumns.SourceType:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.SourceType, opMap)...)
			case model.UserTransactionLogColumns.SourceID:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.SourceID, opMap)...)
			case model.UserTransactionLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.UserTransactionLogColumns.Status, opMap)...)
			}
		}
	}
	if len(mods) == 0 {
		return
	}

	rowsAff, err = model.UserTransactionLogs(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customUserRepository) InsertTaskLog(
	ctx context.Context,
	log *model.UserTaskLog,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return log.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customUserRepository) GetTaskLogList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.UserTaskLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	var latest bool
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		case "latest":
			if val, ok := v.(string); ok && val == "1" {
				latest = true
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserTaskLogColumns.ID:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.ID, opMap)...)
			case model.UserTaskLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.UserID, opMap)...)
			case model.UserTaskLogColumns.TaskName:
				mods = append(mods, modelx.GetStringMods(model.UserTaskLogColumns.TaskName, opMap)...)
			case model.UserTaskLogColumns.TaskSlug:
				mods = append(mods, modelx.GetStringMods(model.UserTaskLogColumns.TaskSlug, opMap)...)
			case model.UserTaskLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.Status, opMap)...)
			}
		}
	}

	if latest {
		mods = append(mods, qm.Select(fmt.Sprintf(
			"max(%s) as %s",
			model.UserTaskLogColumns.ID,
			model.UserTaskLogColumns.ID,
		)))
		mods = append(mods, qm.GroupBy(model.UserTaskLogColumns.UserID))

		if withCount == 2 && limit > 0 {
			mods = append(mods, qm.Limit(limit+1))
		}

		preList, listErr := model.UserTaskLogs(mods...).All(ctx, r.mysqlConn)
		if listErr != nil {
			err = listErr
			return
		}

		if len(preList) > 0 {
			ids := make([]uint64, 0, len(preList))
			for _, log := range preList {
				ids = append(ids, log.ID)
			}
			var subMods []qm.QueryMod
			subMods = append(subMods, model.UserTaskLogWhere.ID.IN(ids))

			var columns []string
			if len(selectCol) > 0 {
				if selectCol == "," {
					columns = append(columns, model.UserTaskLogColumns.ID)
				} else {
					columns = strings.Split(selectCol, ",")
				}
			}
			if len(columns) > 0 {
				subMods = append(subMods, qm.Select(columns...))
			}
			list, err = model.UserTaskLogs(subMods...).All(ctx, r.mysqlConn)
			return
		}

		return
	}

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods)+3)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))
			subMods = append(subMods, qm.Select(model.UserTransactionLogColumns.UserTransactionLogID))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset-1))
				previousList, previousErr := model.UserTaskLogs(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}
				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset+limit))
				nextList, nextErr := model.UserTaskLogs(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}
				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount*2
		} else {
			count, err = model.UserTaskLogs(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.UserTaskLogColumns.ID)
		columns = append(columns, model.UserTaskLogColumns.UserID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.UserTaskLogs(mods...).All(ctx, r.mysqlConn)
	if err == sql.ErrNoRows {
		return nil, 0, model.ErrNotFound
	}

	return
}

func (r *customUserRepository) UpdateTaskLogBatch(
	ctx context.Context,
	cols model.M,
	filter map[string]map[string][]any,
) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.UserTaskLogColumns.ID:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.ID, opMap)...)
			case model.UserTaskLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.UserID, opMap)...)
			case model.UserTaskLogColumns.TaskName:
				mods = append(mods, modelx.GetStringMods(model.UserTaskLogColumns.TaskName, opMap)...)
			case model.UserTaskLogColumns.TaskSlug:
				mods = append(mods, modelx.GetStringMods(model.UserTaskLogColumns.TaskSlug, opMap)...)
			case model.UserTaskLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.UserTaskLogColumns.Status, opMap)...)
			}
		}
	}
	if len(mods) == 0 {
		return
	}

	rowsAff, err = model.UserTaskLogs(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customUserRepository) getCacheKeys(data *model.User) []string {
	if data == nil {
		return []string{}
	}

	cacheIdKey := r.formatPrimary(data.ID)
	cacheIdExpireKey := r.formatPrimaryWithExpire(data.ID)
	cacheKeys := []string{
		cacheIdKey,
		cacheIdExpireKey,
	}

	return cacheKeys
}

func (r *customUserRepository) formatPrimary(primary any) string {
	return fmt.Sprintf("%s%v", r.cacheIdPrefix, primary)
}

func (r *customUserRepository) formatPrimaryWithExpire(primary any) string {
	return fmt.Sprintf("%s%v", r.cacheIdExpirePrefix, primary)
}
